﻿using BitButterECS.Event.EventArgs;
using System.Collections.Generic;
using System.Linq;
using PendingUpdate = System.Tuple<BitButterECS.System.BaseSystem, object, BitButterECS.Event.EventArgs.BaseEventArgs>;
using UpdateLock = System.Tuple<System.Type, System.Type>;

namespace BitButterECS.System
{
    public class SystemManager
    {
        SystemManager() { }

        public static SystemManager Instance => Nested.instance;

        class Nested
        {
            static Nested() { }
            internal static readonly SystemManager instance = new SystemManager();
        }

        protected virtual void RegisterEventHandlers()
        {
        }

        protected virtual void UnregisterEventHandlers()
        {
        }

        public BaseSystem this[int index]
        {
            get
            {
                if (SystemCollection.ContainsKey(index))
                {
                    return SystemCollection[index];
                }
                return null;
            }
        }

        public int AddSystem(BaseSystem system)
        {
            if (!system.IsManaged)
            {
                system.ID = currentID;
                currentID += 1;

                SystemCollection.Add(system.ID, system);
                return system.ID;
            }
            return -1;
        }

        public void RemoveSystem(BaseSystem system)
        {
            if (system.IsManaged)
            {
                SystemCollection.Remove(system.ID);
                system.ID = -1;
            }
        }

        public bool HasSystem(BaseSystem system)
        {
            return systemCollection != null ? systemCollection.ContainsKey(system.ID) : false;
        }

        Dictionary<int, BaseSystem> SystemCollection
        {
            get
            {
                if (systemCollection == null)
                {
                    systemCollection = new Dictionary<int, BaseSystem>();
                }
                return systemCollection;
            }
        }

        Dictionary<int, BaseSystem> systemCollection;
        int currentID = 0;

        //public bool TryUpdateSystem(BaseSystem system)
        //{
        //    return IsUpdateChainable(system.ReadComponents, system.WriteComponents);
        //}

        //public void BeginUpdateSystem(BaseSystem system)
        //{
        //    if (IsUpdateChainable(system.ReadComponents, system.WriteComponents))
        //    {
        //        AcquireLock(ReadingComponents, system.ReadComponents);
        //        AcquireLock(WritingComponents, system.WriteComponents);
        //        UpdatingSystems.Push(system);
        //    }
        //}

        //public void EndUpdateSystem(BaseSystem system)
        //{
        //    var updatingSystem = UpdatingSystems.Peek();
        //    if (updatingSystem == system)
        //    {
        //        ReleaseLock(ReadingComponents, system.ReadComponents);
        //        ReleaseLock(WritingComponents, system.WriteComponents);
        //        UpdatingSystems.Pop();
        //        NotifyNextPendingUpdate();
        //    }
        //}

        //public void QueueSystemUpdate(BaseSystem system, object sender, BaseEventArgs e)
        //{
        //    PendingUpdates.Add(new PendingUpdate(system, sender, e));
        //}

        //List<PendingUpdate> PendingUpdates => pendingUpdates ?? (pendingUpdates = new List<PendingUpdate>());
        //List<PendingUpdate> pendingUpdates;

        //bool IsUpdateChainable(IEnumerable<UpdateLock> readComponents, IEnumerable<UpdateLock> writeComponents)
        //{
        //    if (writeComponents != null)
        //    {
        //        foreach (UpdateLock component in writeComponents)
        //        {
        //            if (IsWriting(component) || IsReading(component))
        //            {
        //                return false;
        //            }
        //        }
        //    }

        //    if (readComponents != null)
        //    {
        //        foreach (UpdateLock component in readComponents)
        //        {
        //            if (IsWriting(component))
        //            {
        //                return false;
        //            }
        //        }
        //    }

        //    return true;
        //}

        //bool IsReading(UpdateLock component)
        //{
        //    return HasMatchingComponent(ReadingComponents, component);
        //}

        //bool IsWriting(UpdateLock component)
        //{
        //    return HasMatchingComponent(WritingComponents, component);
        //}

        //bool HasMatchingComponent(IEnumerable<UpdateLock> components, UpdateLock component)
        //{
        //    return components.Any(t => t.Item1 == component.Item1 && t.Item2 == component.Item2);
        //}

        //void AcquireLock(List<UpdateLock> lockBase, IEnumerable<UpdateLock> requests)
        //{
        //    lockBase.AddRange(requests);
        //}

        //void ReleaseLock(List<UpdateLock> lockBase, IEnumerable<UpdateLock> requests)
        //{
        //    foreach (UpdateLock request in requests)
        //    {
        //        lockBase.Remove(request);
        //    }
        //}

        //void NotifyNextPendingUpdate()
        //{
        //    if (PendingUpdates.Count > 0)
        //    {
        //        var update = PendingUpdates.First();
        //        pendingUpdates.RemoveAt(0);
        //        update.Item1.NotifyUpdate(update.Item2, update.Item3);
        //    }
        //}

        //List<UpdateLock> ReadingComponents => readingComponents ?? (readingComponents = new List<UpdateLock>());
        //List<UpdateLock> readingComponents;

        //List<UpdateLock> WritingComponents => writingComponents ?? (writingComponents = new List<UpdateLock>());
        //List<UpdateLock> writingComponents;

        //Stack<BaseSystem> UpdatingSystems => updatingSystems ?? (updatingSystems = new Stack<BaseSystem>());
        //Stack<BaseSystem> updatingSystems;
    }
}
